/*
The MIT License (MIT)

Copyright (c) 2013 Mike Dapiran, Brian May, Richard Pospesel, and Bert Wierenga

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#pragma once


#include "Hogshead.Common.h"
#include "Hogshead.Core.h"
#include "Hogshead.BulletPhysics.h"

#include "../HLSLCPPDeclarations/Lighting.fx"
#include "../DataStructures/ConstantBuffer/ConstantBuffer.h"
#include "../DataStructures/ConstantBuffer/ConstantBufferGroup.h"
#include "../DataStructures/RenderingTextureBuffer.h"



namespace Hogshead
{
	namespace DX11Rendering
	{
		class Camera;

		class MeshRenderer;

		class PointLight;
		class DirectionalLight;
		class SpotLight;

		class IProcess;
		class Material;

		class RenderingTextureBuffer;

		class VertexShader;
		class PixelShader;

		class BlendState;
		class SamplerState;

		class ShadowMapGenerationProcess;
		
		class Texture;

		/*
		* This class handles the rendering of all renderable components on a scene
		* by scene basis. This class holds an ordered list of classes that extend
		* IProcess. Each process is responsible for handling the rendering of a
		* specific class that extends IComponent.
		*
		* @see IProcess
		* @see Scene
		* @see Camera
		* @see Transform
		* @see DirectionalLight
		* @see PointLight
		* @see SpotLight
		* @see LightingInfo
		*
		* @auther Mike Dapiran
		* @author Bert Wierenga
		*/
		class DX11Director : public IDirector, public IMessageReceiver
		{
			
			DirectorH(Hogshead::DX11Rendering::DX11Director, DX11Director)
		public:
			
			/*
			* Default constructor sets active camera to NULL.
			*/
			DX11Director(void);
			~DX11Director(void);

			/*
			* Called by the Scene we belong to. We will draw a single frame.
			*/
			void draw();

			/*
			* Called by the Scene we belong to so that we can do any updating 
			* necessary.
			*/
			void update(const GameTimer& in_gt);
			
			/*
			* Called by the Scene we belong to so that we can do any initializing
			* necessary.
			*/ 
			void initialize();

			Camera* _active_camera;

			//This contains our components that our collided with last frame
			HashSet<void*> _nonculled_components;

			Vector<IProcess*> _processes;

			/**
			* Used in coneCull.
			*/
			static const float far_clip_radius;

			/*
			* Called to load this Director from an XML block.
			*/
			IDirector* readXML(const String& in_xml);
		
			/*
			* This function is called by any class that extends IComponent that is involved
			* in rendering so that it may be held onto appropriattely.
			*
			* @param in_component An instance of a component that is involved in rendering.
			*/
			void registerComponent(Hogshead::Core::IComponent* in_component);
			
			/*
			* Sets the active camera
			*
			* @param in_camera The Camera that is to become that main camera used for rendering.
			*/
			void setActiveCamera(Camera* in_camera);
		

			void DX11Director::unregisterComponent(Hogshead::Core::IComponent* in_component);

		private:

			static void handle_constant_buffer_modifier(IMessageReceiver*, IMessage*);
			static void handle_culling_message(IMessageReceiver*, IMessage*);

			/**
			* Called by fillOutLighting to determine and write the light data into a MeshRenderer's
			* LightingInfo structure.
			*
			* @param in_nonculled_components All components that have not been culled
			*/
			inline void determinePointLights( HashSet<void*>* in_nonculled_components  );

			/**
			* Called by fillOutLighting to determine and write the light data into a MeshRenderer's
			* LightingInfo structure.
			*
			* @param in_mesh_renderer The MeshRenderer we want to determine the highest
			* priority DirectionalLight's for.
			*/
			inline void determineDirectionalLights( Camera* in_camera );

			/**
			* Called by fillOutLighting to determine and write the light data into a MeshRenderer's
			* LightingInfo structure.
			*
			* @param in_nonculled_components All components that have not been culled
			*/
			inline void determineSpotLights(HashSet<void*>* in_nonculled_components );


			/**
			* This is a group of buffers we want everything in the Scene to be able to modify.
			*/
			ConstantBufferGroup _constant_buffer_group;

#pragma region Lighting
		private:
			/**
			* Used to fill a SpotLight out as black.
			*
			* @param in_lighting_info The LightingInfo structure we are filling out some information on.
			* @param in_index Which index we will be placing this SpotLight information into on the HLSL side.
			*/
			inline void writeBlackSpotLight(LightingInfo* in_lighting_info, int in_index) const;

			/**
			* Used to copy lighting information from a SpotLight to a LightingInfo
			*
			* @param in_lighting_info The LightingInfo structure we are filling out some information on.
			* @param in_point_light The SpotLight we are going to read lighting information from.
			* @param in_index Which index we will be placing this SpotLight information into on the HLSL side.
			*/

			inline void writeSpotLight(LightingInfo* in_lighting_info, SpotLight* in_spot_light, int in_index) const; 

			/**
			* Used to fill a PointLight out as black.
			*
			* @param in_lighting_info The LightingInfo structure we are filling out some information on.
			* @param in_index Which index we will be placing this PointLight information into on the HLSL side.
			*/

			inline void writeBlackPointLight(LightingInfo* in_lighting_info, int in_index) const;

			/**
			* Used to copy lighting information from a PointLight to a LightingInfo
			*
			* @param in_lighting_info The LightingInfo structure we are filling out some information on.
			* @param in_point_light The PointLight we are going to read lighting information from.
			* @param in_index Which index we will be placing this PointLight information into on the HLSL side.
			*/
			inline void writePointLight(LightingInfo* in_lighting_info, PointLight* in_point_light, int in_index) const; 


			/**
			* Used to fill a DirectionalLight out as black.
			*
			* @param in_lighting_info The LightingInfo structure we are filling out some information on.
			* @param in_index Which index we will be placing this DirectionalLight information into on the HLSL side.
			*/
			inline void writeBlackDirectionalLight(LightingInfo* in_lighting_info, int in_index) const;

			/**
			* Used to copy lighting information from a DirectionalLight to a LightingInfo
			*
			* @param in_lighting_info The LightingInfo structure we are filling out some information on.
			* @param in_directional_light The DirectionalLight we are going to read lighting information from.
			* @param in_index Which index we will be placing this PointLight information into on the HLSL side.
			
			*/
			inline void writeDirectionalLight(LightingInfo* in_lighting_info, DirectionalLight* in_directional_light, int in_index) const;

			/**
			* MeshRenderer's that want lighting
			*/
			Vector<MeshRenderer*> _lit_meshes;

			/**
			* MeshRenderer's that do not want lighting
			*/
			Vector<MeshRenderer*> _unlit_meshes;

			/**
			* All the point lights in the Scene.
			*/
			Vector<PointLight*> _point_lights;	

			/**
			* All the Spot lights in the Scene.
			*/
			Vector<SpotLight*> _spot_lights;	

			/**
			* All the directional lights in the Scene.
			*/
			Vector<DirectionalLight*> _directional_lights;

			//bins so memory allocation isn't happening over and over
			Vector<PointLight*> _pl_bin;
			Vector<DirectionalLight*> _dl_bin;
			Vector<SpotLight*> _sl_bin;


			/* A place we store information about the most pertinent lighting info each frame */
			LightingInfo _lighting_info;			

			/** 
			* This is called at the beginning of each draw call to give
			* each component that wants lighting info the most pertinent
			* info
			*/
			inline void fillOutLighting(Camera* in_camera, HashSet<void*>* in_nonculled_components);

			ShadowMapGenerationProcess* _shadow_generator;

		public:
			const LightingInfo& getLighting() const;

			void updateSpecularExponent(float in_specular_exponent);		
			
#pragma endregion

		private:
			/**
			* A lookup table of texture buffers for rendering that will be requested to be bound 
			* via processes. 
			*/
			HashTable<String, RenderingTextureBuffer> _rendering_buffers;			
			

			Vector<ID3D11RenderTargetView*> _rtv_bin;
			ID3D11DepthStencilView* _dsv_bin;
			Vector<ID3D11ShaderResourceView*> _srv_bin;

			/* 
			* used to wipe all Shader Resource Views before we attempt to bind anything 
			*/
			ID3D11ShaderResourceView* _clear_array[128];
			CullingFrustum* _cull_frustrum;

			/* 
			* A list of Shader Resource Views we want to use per Scene, setup in xml. The int stored
			* is used to determine which register to send the texture to
			*/
			Vector<Pair<Texture*, int>> _scene_wide_srv_list;

#ifdef HOGSHEADED
		public:
			

		private:
			//an array of editor cameras
			Camera** _editor_cameras;
#endif

		};
	}
}

